local filename = "noderequest.lua"

local skynet = require "skynet"
local cluster = require "skynet.cluster"
require "skynet.manager"

local servicepoolmng = require "incrservicepoolmng"
local configdao = require "configdao"
local timetool = require "timetool"
local filelog = require "filelog"
local tabletool = require "tabletool"
local List = require "list"

local json = require "cjson"
json.encode_sparse_array(true, 1, 1)

require "bmenum"

local CMD = {}
local service = nil

--是否进桌中
local is_entertable = false

--游戏桌配置列表
local gametable_conf = nil

-----------------
--局部函数

--
--初始化游戏桌配置
--
local function init_gametable_conf()
    gametable_conf = tabletool.deepcopy(configdao.get_common_conf("gametable_conf"))
    if not gametable_conf then
        gametable_conf = {}
        return
    end
end

--
--创建游戏桌
--
local function create_game_table(level,table_conf,roledata)
    level = level or 1
    table_conf = table_conf or gametable_conf[level]
    if not table_conf then
        return false
    end

    if not roledata then
        roledata = {
            info = { 
                rolename = "root",  --名字
                level = 0,          --等级
		        logo = "1",         --头像
		        sex = 2,            --性别
                rid = 22222222,
            },
            money = {
                diamond = 999999999,
                coin = 999999999,
                lock_coin = 0,
                lock_diamond = 0,
             },
            table_persist = 1800,   --保留30分钟
            ip_addr = "",
        }
    end

    local func = service.CMD.createtable
    local msgbody = {table_conf = table_conf,level_type = level}
    local response = func(roledata.info.rid,roledata,msgbody, true)
    if not response or response.retcode then
        return false
    end

    return response
end

--
--获得一个空闲桌
--
local function get_one_idle_table(level_type)
    local idle_table_list = service.game_table_list.idle_table[level_type]

    --从队首取一个空闲桌
    local table_index = nil
    if idle_table_list and idle_table_list:get_size() > 0 then
        while true do
            table_index = idle_table_list:pop_left()
            if not table_index or service.index_to_table[table_index] then
                break
            end
        end
    end
	
    --没有空闲桌，创建新桌子
    if not table_index then
        local result = create_game_table(level_type)
        if not result then
            return false
        end

        idle_table_list = idle_table_list or List:new()
        idle_table_list:push_left(result.table_index)
        service.game_table_list.idle_table[level_type] = idle_table_list
        return get_one_idle_table(level_type)
   end

    --检测桌子是否可进入
    local tableitem = service.index_to_table[table_index]
    local eseat_num = skynet.call(tableitem.table_service, "lua", "cmd", "check_tablestate")
    if not eseat_num then           --桌子不存在
        return get_one_idle_table(level_type)
    elseif eseat_num <= 0 then      --桌子已满
        --local new_index = get_one_idle_table(level_type)
        idle_table_list:push_right(table_index)
        --return new_index
        return -1
    else
        idle_table_list:push_right(table_index)
        return table_index
    end

end

------------------------

function CMD.init(caller)
	service = caller -- 这里的caller，是router
    service.wait_entertable = { }   --等待进桌队列,防止多次进桌

    --初始化场次桌子配置
    init_gametable_conf()
end

function CMD.request(msg,...)
    local f = CMD[msg]
    if not f then
        return { retcode = EErrCode.ERR_INVALID_REQUEST,retstr = "消息不存在！"}
    end

    return f(...)
end

--------------------------------------

--
-- 生成一个桌子号，随机码
-- 这里暂时是本节点生成，以后要去 hall 生成,带入的两个参数，也是准备传到 hallnode 的
--
local function gen_table_code(table_index, create_rid, table_conf)
	-- local node 生成

	-- 去hall节点生成code，同时 hall节点保存了code -> index 对应关系
	-- hall节点也可以是gata节点
	local status,newcode = skynet.pcall(cluster.call,"hallnode", ".router", "create_table_code",skynet.getenv("nodename"), table_index, create_rid, table_conf)
    if not status then return end
	return newcode
end

-------------------

--
-- 初始化table池
--
function CMD.init_table_pool(nodeconf)
	service.idle_table_mng = servicepoolmng:new({}, {service_name="table", service_size=nodeconf.tablesize, incr=nodeconf.tableinrc})
end

--
-- 这里收到的就就是协议的 createtable，命令，所以不能修改
--
function CMD.createtable(rid, roledata, msgbody, islocal)
    --客户端不能建桌
    if not islocal then
        return {}
    end

    --设置停服标记后，不能创建桌子
    if service.gameend_flag then
        return {retcode = EErrCode.ERR_INVALID_REQUEST, retstr = "服务器维护中"}
    end

    --进桌中，不能建桌
    if service.wait_entertable[rid] or service.role_map[rid] then
        return 
    end

	--检查参数
    local response = { }
    local table_conf = msgbody.table_conf or { }
    local playermoney = roledata.money or { }
    local curmoney = playermoney.diamond or 0
    local min_carry = table_conf.min_carry_amount or 0
    local max_carry = table_conf.max_carry_amount or 0

    if not table_conf.base_score_list or #(table_conf.base_score_list) <= 0 then
        response.retcode = EErrCode.ERR_INVALID_PARAMS
		response.retstr = "底注列表为空！"
		return	response
    end
    if min_carry > max_carry then
        response.retcode = EErrCode.ERR_INVALID_PARAMS
		response.retstr = "最小携带量不能大于最大携带量！"
		return	response
    end
    if table_conf.table_type == EBMTableType.TABLE_TYPE_COIN then
        curmoney = playermoney.coin or 0
    end
    if curmoney < min_carry then
        response.retcode = EErrCode.ERR_NOT_ENOUGH_DIAMOND
		response.retstr = "您的金币不足，不能创建房间！"
		return	response
    end

    local table_service = service.idle_table_mng:create_service()
	if table_service == nil then
		filelog.sys_error("create_table roomsvr's idle_table_mng not enough table_service!")
		return {retcode = EErrCode.ERR_SYSTEM_ERROR,retstr = "system error!"}
	end

	-- msgbody 里面包含了创建房间的配置， 具体在 proto 里的 tablecfg 里面定义
	-- 先生成一个table_service, 然后生成 code， 然后生成对应关系，
	-- 然后把 msgbody里的conf 传入进去 然后再去初始化这个table_service
	local table_index = service.table_index
	local table_code = gen_table_code(service.table_index, rid, msgbody.table_conf)
    if not table_code then
        skynet.call(table_service.service, "lua", "cmd", "kill_service")
        return	{retcode = EErrCode.ERR_SYSTEM_ERROR,retstr = "系统错误！"}
    end
   
	service.index_to_table[table_index] = {table_service = table_service.service, code = table_code, level_type = msgbody.level_type}
	service.code_to_index[table_code] = table_index
	service.table_index = service.table_index + 1

    local tradeid = service:get_tradeid()
	local attachconf = {
        index = table_index, 
		code = table_code,
		rid = rid,
		rolename = roledata.info.rolename,
        logo = roledata.info.logo,
        table_service =table_service.service,
        agencylevel = roledata.info.agencylevel,
        tradeid = tradeid,
        table_persist = roledata.table_persist,  --桌子没人保留时间
        level_type    = msgbody.level_type,      --场次类型
    }

	-- 如果失败，回滚刚才的操作（不过这个失败可能性很小，因为这个操作，只是对一些对象赋值
    service.wait_entertable[rid] = true
	local result = skynet.call(table_service.service, "lua", "start", msgbody.table_conf, attachconf)
    filelog.sys_info("table_code:",table_code,"table_index:",table_index,table_service.service)
    service.wait_entertable[rid] = nil

	return {table_index = table_index,table_code = table_code,nodename = skynet.getenv("nodename")}
end

--
-- 进入桌子操作的时候，用户还不在某张桌子上，所以不能在table service处理，所以还需要在router里面处理
-- 这里做了个兼容，可以使用 table_index , 也可以使用table_code ，如果有 table_index 则先用index直接进入
-- 如果没有index，只有 table_code ,则用table_code 找到对应的index ,再进入。
--
function CMD.enter_gametable(rid, roledata, msgbody)
    --设置停服标记后，不能进入桌子
    if service.gameend_flag then
        return {retcode = EErrCode.ERR_INVALID_REQUEST, retstr = "服务器维护中"}
    end

    --进桌中
    if service.wait_entertable[rid] or service.role_map[rid] then
        return 
    end

    roledata.money = roledata.money or {}
	local table_index = nil
	if msgbody.table_index then
		table_index = msgbody.table_index
	elseif msgbody.table_code and service.code_to_index[msgbody.table_code] then
		table_index = service.code_to_index[msgbody.table_code]
	else
		return {retcode = EErrCode.ERR_INVALID_TABLE_INDEX, retstr="无效桌号"}
	end

	local table_service = nil
	if service.index_to_table[table_index] then 
		table_service = service.index_to_table[table_index].table_service
	else
        --通知gate，玩家离桌
        service:unforward(rid, table_index, roledata.money.coin)
		return {retcode = EErrCode.ERR_INVALID_TABLE_INDEX, retstr="房间不存在"}
	end

    service.wait_entertable[rid] = true
	local status,result = skynet.pcall(skynet.call,table_service, "lua", "entertable", rid, roledata)
    service.wait_entertable[rid] = nil

	-- 如果成功,只有正确结构返回，没有 retcode，所以要通知gate去更改状态。
	if result and result.retcode == nil then
		service:forward(rid, table_service, table_index, roledata.money.coin)
	end

	return result
end

--
--进桌
--msgbody一个参数level_type：进入场次的类型
--
function CMD.entertable(rid, roledata, msgbody)
    --设置停服标记后，不能进入桌子
    if service.gameend_flag then
        return {retcode = EErrCode.ERR_INVALID_REQUEST, retstr = "服务器维护中"}
    end

    --进桌中
    if service.wait_entertable[rid] or service.role_map[rid] then
        return 
    end

    local level = msgbody.level_type or 0
    if level <= 0 then
        return {retcode = EErrCode.ERR_INVALID_PARAMS, retstr = "无效的参数"}
    end

    --进桌中等待
    local cnt = 0
    while is_entertable do
        cnt = cnt + 1
        if cnt > 40 then
            return {retcode = EErrCode.ERR_SYSTEM_ERROR, retstr = "网络繁忙，请稍后重试"}
        end
        skynet.sleep(5)
    end

    --设置进桌标记
    is_entertable = true

	--从桌子列表中找一个有空位的桌子
    local _,table_index = skynet.pcall(get_one_idle_table, level)
    is_entertable = false
    if not table_index then
        return {retcode = EErrCode.ERR_SYSTEM_ERROR, retstr = "系统错误"}
    elseif table_index == -1 then
        return {retcode = EErrCode.ERR_TABLE_FULL, retstr = "房间人数已满"}
    end
    
    return CMD.enter_gametable(rid, roledata, {table_index = table_index})
end

--
--玩家断开连接
--
function CMD.disconnect(rid, roledata)
    filelog.print("----------------Disconnect----",rid)

    return true
end

--
--断线重连
--
function CMD.reentertable(rid, roledata,request)
    local notciemsg = { }
    notciemsg.retcode = EErrCode.ERR_INVALID_REQUEST
    notciemsg.retstr = "不在桌子内！"
    service:unforward(rid, request.table_index,roledata.money.diamond)
    return notciemsg
end

--
--获得场次桌子信息
--
function CMD.getlevellist()
    local levellist = { }

    for i,conf in pairs( gametable_conf) do
        levellist[i] = {
            level_type 	            = i,	                    --场次类型 1初级 2中级 3高级
	        min_carry_amount  		= conf.min_carry_amount,	--最小携带量
	        max_carry_amount		= conf.max_carry_amount, 	--最大携带量
	        up_banker_money		    = conf.up_banker_money,	    --上庄需要金币
	        conf_intro              = json.encode(conf),        --桌子配置                       
        }

        --该场次当前人数
        levellist[i].cur_total_player =  service.diff_level_players[i] or 0

    end
    
    return {levellist = levellist}
end

return CMD